package com.artemchep.keyguard.common.usecase.impl

import com.artemchep.keyguard.common.io.IO
import com.artemchep.keyguard.common.io.ioEffect
import com.artemchep.keyguard.common.model.PasswordGeneratorConfig
import com.artemchep.keyguard.common.service.crypto.CryptoGenerator
import com.artemchep.keyguard.common.usecase.GetPinCode
import kotlinx.coroutines.ensureActive
import org.kodein.di.DirectDI
import org.kodein.di.instance
import kotlin.coroutines.coroutineContext
import kotlin.math.pow
import kotlin.math.roundToInt

class GetPinCodeImpl(
    private val cryptoGenerator: CryptoGenerator,
) : GetPinCode {
    // Sorted and truncated common PINs from
    // https://github.com/danielmiessler/SecLists/blob/master/Passwords/Common-Credentials/four-digit-pin-codes-sorted-by-frequency-withcount.csv
    private val commonThreeDigitsPins by lazy {
        intArrayOf(
            0,
            7,
            10,
            12,
            90,
            98,
            100,
            101,
            102,
            111,
            112,
            121,
            122,
            123,
            124,
            131,
            132,
            135,
            141,
            151,
            181,
            196,
            197,
            198,
            199,
            200,
            202,
            211,
            212,
            222,
            232,
            234,
            242,
            246,
            252,
            258,
            333,
            432,
            444,
            454,
            515,
            555,
            567,
            666,
            696,
            741,
            777,
            852,
            888,
            987,
            999,
        )
    }

    // Sorted common PINs from
    // https://github.com/danielmiessler/SecLists/blob/master/Passwords/Common-Credentials/four-digit-pin-codes-sorted-by-frequency-withcount.csv
    private val commonFourDigitsPins by lazy {
        intArrayOf(
            0,
            1,
            7,
            9,
            11,
            12,
            13,
            24,
            69,
            70,
            77,
            83,
            99,
            101,
            102,
            103,
            104,
            110,
            111,
            112,
            114,
            116,
            119,
            120,
            121,
            122,
            123,
            124,
            125,
            127,
            128,
            129,
            130,
            147,
            202,
            203,
            204,
            205,
            208,
            209,
            210,
            212,
            213,
            214,
            217,
            218,
            219,
            220,
            221,
            224,
            225,
            228,
            258,
            301,
            302,
            303,
            304,
            305,
            310,
            311,
            312,
            313,
            314,
            315,
            316,
            317,
            318,
            319,
            321,
            322,
            323,
            325,
            326,
            327,
            328,
            330,
            331,
            402,
            404,
            405,
            408,
            410,
            411,
            412,
            413,
            414,
            415,
            416,
            417,
            419,
            420,
            423,
            425,
            426,
            428,
            486,
            501,
            502,
            505,
            507,
            509,
            510,
            512,
            513,
            515,
            517,
            521,
            523,
            524,
            525,
            526,
            604,
            606,
            610,
            611,
            612,
            613,
            616,
            622,
            624,
            625,
            666,
            704,
            707,
            708,
            710,
            711,
            712,
            714,
            721,
            722,
            807,
            808,
            809,
            811,
            812,
            813,
            815,
            818,
            820,
            822,
            825,
            829,
            904,
            907,
            908,
            909,
            911,
            912,
            913,
            914,
            915,
            916,
            917,
            918,
            919,
            921,
            922,
            923,
            924,
            926,
            927,
            928,
            929,
            987,
            1000,
            1001,
            1002,
            1003,
            1004,
            1005,
            1006,
            1007,
            1008,
            1009,
            1010,
            1011,
            1012,
            1013,
            1014,
            1015,
            1016,
            1017,
            1018,
            1019,
            1020,
            1021,
            1022,
            1023,
            1024,
            1025,
            1026,
            1027,
            1028,
            1029,
            1030,
            1031,
            1052,
            1066,
            1080,
            1092,
            1100,
            1101,
            1102,
            1103,
            1104,
            1105,
            1106,
            1107,
            1108,
            1109,
            1110,
            1111,
            1112,
            1113,
            1114,
            1115,
            1116,
            1117,
            1118,
            1119,
            1120,
            1121,
            1122,
            1123,
            1124,
            1125,
            1126,
            1127,
            1128,
            1129,
            1130,
            1133,
            1134,
            1144,
            1188,
            1199,
            1200,
            1201,
            1202,
            1203,
            1204,
            1205,
            1206,
            1207,
            1208,
            1209,
            1210,
            1211,
            1212,
            1213,
            1214,
            1215,
            1216,
            1217,
            1218,
            1219,
            1220,
            1221,
            1222,
            1223,
            1224,
            1225,
            1226,
            1227,
            1228,
            1229,
            1230,
            1231,
            1232,
            1233,
            1234,
            1235,
            1236,
            1243,
            1245,
            1248,
            1254,
            1256,
            1290,
            1311,
            1312,
            1313,
            1314,
            1318,
            1321,
            1323,
            1324,
            1331,
            1345,
            1346,
            1357,
            1369,
            1379,
            1411,
            1412,
            1414,
            1415,
            1423,
            1432,
            1437,
            1452,
            1453,
            1472,
            1478,
            1492,
            1515,
            1590,
            1616,
            1701,
            1717,
            1776,
            1800,
            1812,
            1818,
            1907,
            1911,
            1914,
            1919,
            1937,
            1940,
            1941,
            1942,
            1943,
            1944,
            1945,
            1946,
            1947,
            1948,
            1949,
            1950,
            1951,
            1952,
            1953,
            1954,
            1955,
            1956,
            1957,
            1958,
            1959,
            1960,
            1961,
            1962,
            1963,
            1964,
            1965,
            1966,
            1967,
            1968,
            1969,
            1970,
            1971,
            1972,
            1973,
            1974,
            1975,
            1976,
            1977,
            1978,
            1979,
            1980,
            1981,
            1982,
            1983,
            1984,
            1985,
            1986,
            1987,
            1988,
            1989,
            1990,
            1991,
            1992,
            1993,
            1994,
            1995,
            1996,
            1997,
            1998,
            1999,
            2000,
            2001,
            2002,
            2003,
            2004,
            2005,
            2008,
            2010,
            2020,
            2103,
            2110,
            2112,
            2121,
            2123,
            2210,
            2211,
            2212,
            2222,
            2233,
            2244,
            2311,
            2323,
            2332,
            2345,
            2356,
            2412,
            2424,
            2468,
            2469,
            2486,
            2501,
            2510,
            2525,
            2580,
            2626,
            2727,
            2828,
            2848,
            2929,
            3000,
            3030,
            3117,
            3131,
            3210,
            3211,
            3214,
            3232,
            3333,
            3434,
            3456,
            3535,
            3579,
            3636,
            3690,
            3737,
            3838,
            3939,
            4000,
            4001,
            4040,
            4141,
            4200,
            4242,
            4321,
            4444,
            4455,
            4545,
            4567,
            4646,
            4711,
            4747,
            4848,
            4860,
            4862,
            5000,
            5050,
            5150,
            5151,
            5252,
            5425,
            5454,
            5555,
            5656,
            5678,
            5683,
            5757,
            5858,
            5888,
            6464,
            6666,
            6669,
            6789,
            6969,
            6996,
            7070,
            7272,
            7410,
            7474,
            7575,
            7777,
            7788,
            7878,
            7890,
            7894,
            7913,
            7942,
            7979,
            8080,
            8282,
            8520,
            8888,
            8989,
            9090,
            9111,
            9876,
            9898,
            9981,
            9988,
            9999,
        )
    }

    constructor(directDI: DirectDI) : this(
        cryptoGenerator = directDI.instance(),
    )

    override fun invoke(
        config: PasswordGeneratorConfig.PinCode,
    ): IO<String> = ioEffect {
        val range = kotlin.run {
            val end = 10.0.pow(config.length).roundToInt().minus(1)
            0..end
        }
        var result = 0
        var iteration = 0
        do {
            // A safe exit if we mess up the validation
            // code. Check if the coroutine is still active
            // and cancel properly.
            if (iteration > 50) {
                iteration = 0
                coroutineContext.ensureActive()
            } else iteration += 1
            result = cryptoGenerator.random(range)
        } while (verifyIsCommon(result, config.length))
        result.toString().padStart(config.length, '0')
    }

    private fun verifyIsCommon(pin: Int, length: Int): Boolean {
        // TODO: Check for common patterns in other than the 3-4
        //  digits long PINs.
        return when (length) {
            3 -> verifyIsInList(pin, commonThreeDigitsPins)
            4 -> verifyIsInList(pin, commonFourDigitsPins)
            else -> false
        }
    }

    private fun verifyIsInList(pin: Int, array: IntArray): Boolean {
        // Simple binary search, we assume that
        // the array is sorted.
        var start = 0
        var end = array.lastIndex
        while (start < end) {
            val index = (start + end) / 2
            val value = array[index]
            if (pin > value) {
                start = index + 1
            } else if (pin < value) {
                end = index - 1
            } else return true // We have found the PIN!
        }
        return false
    }

}
